﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace Icst.Leaflike.Util
{
    public class Impersonator : IDisposable
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool CloseHandle(IntPtr handle);

        // logon types
        const int LOGON32_LOGON_INTERACTIVE = 2;
        const int LOGON32_LOGON_NETWORK = 3;
        const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

        // logon providers
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_PROVIDER_WINNT50 = 3;
        const int LOGON32_PROVIDER_WINNT40 = 2;
        const int LOGON32_PROVIDER_WINNT35 = 1;

        private IntPtr _userToken = IntPtr.Zero;
        private WindowsImpersonationContext _impersonatedUser;
        public Impersonator(string username, string password)
        {
            string userName, domainName;

            _userToken = IntPtr.Zero;
            _impersonatedUser = null;

            try
            {
                if (!(username == null || username.Trim().Length == 0))
                {
                    userName = username.Trim();
                    string[] parts = username.Split(new char[] { '\\' });
                    if (parts.Length == 1)
                    {
                        userName = parts[0];
                        domainName = ".";
                    }
                    else if (parts.Length == 2)
                    {
                        userName = parts[1].Trim();
                        domainName = parts[0].Trim();
                    }
                    else
                    {
                        throw new ArgumentException("'{0}' is an invalid value for username", username);
                    }
                    bool isSuccess = LogonUser(userName, domainName, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref _userToken);
                    if (!isSuccess)
                    {
                        RaiseLastError("LogonUser");
                    }
                    _impersonatedUser = WindowsIdentity.Impersonate(_userToken);

                }
            }
            catch (Exception)
            {
                CleanUp();
                throw;
            }

        }
        public void Dispose()
        {
            CleanUp();
        }
        private void CleanUp()
        {
            if (_impersonatedUser != null)
            {
                _impersonatedUser.Undo();
                _impersonatedUser.Dispose();
                _impersonatedUser = null;
            }
            if (_userToken != IntPtr.Zero)
            {
                bool isSuccess = CloseHandle(_userToken);
                _userToken = IntPtr.Zero;
            }
        }
        private void RaiseLastError(string operation)
        {
            int errorCode = Marshal.GetLastWin32Error();
            string errorMessage = string.Format("Error code {0} returned from operation {1}", errorCode, operation);
            throw new ApplicationException(errorMessage);
        }
    }
}
